/**
 * JSimpleDoc.java
 * 
 * Created: 2009-12-13 09:21:07 am
 * 
 * @author James Tang
 *
 * @copyright (C) 2009 James Tang.
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, 
 * MA02111-1307, USA.
 */

package cn.fwso.util;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileFilter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Calendar;
import java.util.GregorianCalendar;

/**
 * <p>
 * JSimpleDoc, used to generate doc liked notes from a project or a collection of
 * files. This is generally used to trace the modification of source files, such
 * as the files of an open source project.
 * </p>
 * 
 * <p>
 * The idea is derived from the process of development zen-cart modules.
 * </p>
 * 
 * <p>
 * Usage:
 * </p>
 * 
 * <ul>
 * 
 * <li>
 * 1. Locate the note pairs in the source file while you modified, The
 * NOTE_PREFIEX and NOTE_ID is specified by yourself, while the START and END is
 * required.
 * 
 * <pre>
 * 			///NOTE_PREFIX#NOTE_ID#START# Some notes goes here
 * 			Code modified area
 * 			///NOTE_PREFIX#NOTE_ID#END# Some notes goes here
 * </pre>
 * 
 * </li>
 * 
 * <li>
 * 2. Construct a new Instance of JNotes by specifying the required parameters
 * according to your situation.
 * 
 * This class provides and default method for you, you run it in console and
 * specifying the following parameters, note that the parameters should follow
 * the order as the example: 
 * 	<blockquote> 
 * 	&gt; java JNotes /source/base/dir /note/output/dir my_prefix my_id php "Optional note name" </blockquote></li>
 * </ul>
 * 
 * @author <a href="http://fwso.cn" target="_blank"
 *         title="Visit James Lab for more fun things">James Tang</a>
 * 
 */
public class JSimpleDoc {

	private String baseDir;
	private String outputDir;
	private String noteId;

	private String patternPrx = "///JN#";
	private String startPattern = "";
	private String endPattern = "";
	private String notes = null;

	private String fileExt = "php";

	private File outputFile = null;

	private BufferedWriter writer = null;

	private FileFilter filter = null;
	
	private static final String GENERATED_BY = "<div>Generated by <a href=\"http://fwso.cn\" target=\"_blank\">JSimpleDoc</a></div>";

	/**
	 * Constructor for PHP files
	 * 
	 * @param baseDir
	 *            , the base directory under which the notes will be generated
	 *            from the files
	 * @param outputDir
	 *            , the directory for the output file to be located
	 * @param noteId
	 *            , the ID for notes
	 */
	public JSimpleDoc(String baseDir, String outputDir, String noteId) {
		this(baseDir, outputDir, "JN", noteId, "php", null);
	}

	/**
	 * General constructor.
	 * 
	 * @param baseDir
	 * @param outputDir
	 * @param noteId
	 * @param fileExt
	 *            , file extension
	 * @param notes
	 *            , note name
	 */
	public JSimpleDoc(String baseDir, String outputDir, String patternPrefix,
			String noteId, String fileExt, String notes) {
		this.baseDir = baseDir;
		this.outputDir = outputDir;
		this.patternPrx = "///" + patternPrefix + "#";
		this.noteId = noteId;
		this.fileExt = fileExt;
		this.notes = notes;

		this.startPattern = this.patternPrx + this.noteId + "#START#";
		this.endPattern = this.patternPrx + this.noteId + "#END#";

		init();
	}

	// Public Methods
	
	
	public String toString() {
		return baseDir + ", " + outputDir + ", " + this.patternPrx + ", "
				+ noteId + ", " + fileExt + (this.notes== null ? "" : ", "+notes);
	}

	// Private Methods
	
	
	private void init() {

		if (!this.outputDir.endsWith("\\") || !this.outputDir.endsWith("/")) {
			this.outputDir += File.separator;
		}

		if (!this.baseDir.endsWith("\\") || !this.baseDir.endsWith("/")) {
			this.baseDir += File.separator;
		}

		GregorianCalendar gc = new GregorianCalendar();

		String outputFileName = this.outputDir + this.noteId
				+ gc.get(Calendar.YEAR) + gc.get(Calendar.MONTH)
				+ gc.get(Calendar.DAY_OF_MONTH) + gc.get(Calendar.HOUR_OF_DAY)
				+ gc.get(Calendar.MINUTE) + gc.get(Calendar.SECOND) + ".html";

		outputFile = new File(outputFileName);

		File currentDir = new File(this.baseDir);

		filter = new JFileFilter(this.fileExt);

		try {
			writer = new BufferedWriter(new FileWriter(outputFile));

			startHTML();

			if (currentDir.isDirectory()) {
				extractNotes(currentDir);
			} else {
				System.err.println("You base directory '" + this.baseDir
						+ "' is invalid.");
			}

			endHTML();

			writer.close();
		} catch (IOException ex) {
			System.err.println("Initial Exception: ");
			ex.printStackTrace();
		}
	}

	private void extractNotes(File currentDir) {

		if (!currentDir.isDirectory()) {
			return;
		}

		File[] fileList = currentDir.listFiles(filter);

		for (int i = 0; i < fileList.length; i++) {

			if (fileList[i].isDirectory()) {
				extractNotes(fileList[i]);
			} else {

				try {

					BufferedReader br = new BufferedReader(new FileReader(
							fileList[i]));

					String line = null;
					boolean readStart = false;
					int lineCount = 0;

					// TODO encode the line for HTML, for example, < to &lt; >
					// to &gt;
					while ((line = br.readLine()) != null) {
						lineCount++;
						if (line.contains(this.startPattern)) {
							
							if ( readStart ) {
								writer.append("</pre>");
								writer.newLine();
							}
							
							writer.append("<h4>[");
							writer.append(Integer.toString(lineCount));
							writer.append("]");
							writer.append(fileList[i].getPath().substring(
									this.baseDir.length() - 1));
							writer.append("</h4>\n<pre lang=\"");
							writer.append(this.fileExt);
							writer.append("\">");
							writer.newLine();
							writer.append(encodeHTML(line));
							writer.newLine();
							readStart = true;
							continue;
						}

						if (line.contains(this.endPattern) && readStart) {
							readStart = false;
							writer.append(encodeHTML(line));
							writer.newLine();
							writer.append("</pre>");
							writer.newLine();
							continue;
						}

						if (readStart) {
							writer.append(encodeHTML(line));
							writer.newLine();
						}

					}

					if (readStart) {
						readStart = false;
						writer.append("</pre>");
						writer.newLine();
					}
					
					br.close();
				} catch (IOException ex) {

					System.err.println("Reading Exception: " + fileList[i]);
					ex.printStackTrace();

				}
			}

		}

	}

	private String encodeHTML(String line) {

		return line.replace("<", "&lt").replace(">", "&gt;");
	}

	private void startHTML() throws IOException {

		if (this.writer != null) {
			writer
					.append("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n");
			writer.append("<html xmlns=\"http://www.w3.org/1999/xhtml\">\n");
			writer.append("<head>\n");
			// TODO implements the UTF-8 encoding
			// writer.append("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n");
			writer.append("<title>Notes of ");
			writer.append(this.notes == null ? this.noteId : this.notes);
			writer.append("</title>\n");

			// Style
			writer
					.append("<style type=\"text/css\">h4{color:#FF9600; font-weight:bold; margin:0;padding:10px 0;} pre {border:1px solid #CCC;}</style>");

			writer.append("</head>\n<body>\n");
		}

	}

	private void endHTML() throws IOException {
		if (writer != null) {
			writer.append(GENERATED_BY);
			writer.newLine();
			writer.append("</body>\n</html>\n");
		}
	}

	/**
	 * The default main method, if you want to use it, you should specify at
	 * least 5 arguments.
	 * 
	 * @param cArgs
	 *            , command line arguments
	 */
	public static void main(String[] cArgs) {
		
		System.out.println("Start Searching Notes...");

		if (cArgs.length < 5) {
			System.out.println("\tError: More parameters expected");
			System.exit(1);
		} else {

			JSimpleDoc jn = new JSimpleDoc(cArgs[0], cArgs[1], cArgs[2], cArgs[3], cArgs[4],
					cArgs.length > 5 ? cArgs[5] : cArgs[3]);
			
			System.out.println("\t" + jn.toString());
		}

		
		System.out.println("Notes Search Done.");
		
	}
}

class JFileFilter implements FileFilter {

	private String fileExtension;

	/**
	 * Create an file filter by specifying the file extension
	 * 
	 * @param ext
	 *            , the file extension
	 */
	public JFileFilter(String ext) {
		fileExtension = ext;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.io.FileFilter#accept(java.io.File)
	 */
	@Override
	public boolean accept(File pathname) {

		if (pathname.isDirectory()
				|| pathname.getName().endsWith(fileExtension)) {
			return true;
		}

		return false;
	}

}
